package com.atmilan.quartz.utils.product;

import com.alibaba.fastjson.JSONObject;
import com.atmilan.quartz.config.RestTemplateConfig;
import com.atmilan.quartz.constant.Constants;
import com.atmilan.quartz.model.dto.jst.JstProductResDTO;
import com.atmilan.quartz.model.dto.jst.JstProductStockResDTO;
import com.atmilan.quartz.utils.RedisUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.GZIPInputStream;

/**
 * 拉取商品源数据
 */

@Slf4j
@Service
public class JstProductUtil {

    @Autowired
    RedisUtil redisUtil;

    @Value("${jst.url.domain}")
    private String domainUrl;
    @Value("${jst.url.refreshTokenUrl}")
    private String refreshTokenUrl;
    @Value("${jst.url.authUrl}")
    private String authUrl;
    @Value("${jst.url.authLoginUrl}")
    private String authLoginUrl;
    @Value("${jst.url.productQueryUrl}")
    private String productQueryUrl;
    @Value("${jst.url.inventoryQueryUrl}")
    private String inventoryQueryUrl;
    @Value("${jst.appKey}")
    private String appKey;
    @Value("${jst.appSecret}")
    private String appSecret;

    private static final String CHARSET_UTF8 = "utf-8";
    private static final String CONTENT_ENCODING_GZIP = "gzip";

    /**
     * 刷新token
     * @author daigb
     * @date:  2022-02-23 15:53
     */
    public void refreshToken() throws MalformedURLException, IOException {
        long second = Instant.now().getEpochSecond();
        Map<String, String> params = getParams(second);
        String refreshToken = redisUtil.get(Constants.JST_REFRESH_TOKEN);
//    	refreshToken = StringUtil.isEmpty(refreshToken) ? "d0ce08dba7b547c7b60105db2bdeb2a1" : refreshToken;
        refreshToken = "3e816520191d45d6b2a20728e4272dda";

        // 获取签名
        params.put("grant_type", "refresh_token");
        params.put("refresh_token", refreshToken);
        params.put("scope", "all");
        params.put("sign", refreshToken(params));
        // 刷新令牌
        JSONObject jSONObject = JSONObject.parseObject(callApi(new URL(domainUrl + refreshTokenUrl), params));
        if(!"0".equals(jSONObject.getString("code"))) {
            log.error("--------------- 聚水潭刷新token失败: {} ---------------", jSONObject.toString());
            return;
        }
        String accessToken = jSONObject.getJSONObject("data").getString("access_token");
        refreshToken = jSONObject.getJSONObject("data").getString("refresh_token");

        // 重新设置令牌
        redisUtil.set(Constants.JST_REFRESH_TOKEN, refreshToken);
        redisUtil.set(Constants.JST_ACCESS_TOKEN, accessToken);

        log.info("--------------- 聚水潭刷新token成功: 新的accessToken: {}, refreshToken: {} ---------------", accessToken, refreshToken);
    }

    /**
     * 查询商品列表
     * @author daigb
     * @return
     * @date:  2022-02-28 14:34
     */
    public JstProductResDTO productPage(Integer pageNow, String modifiedBegin, String modifiedEnd) throws MalformedURLException, IOException {
        long second = Instant.now().getEpochSecond();
        Map<String, String> params = getParams(second);
        String accessToken = redisUtil.get(Constants.JST_ACCESS_TOKEN);

        // 获取签名
        params.put("access_token", accessToken);
        params.put("version", "2");
        // 业务参数
        JSONObject json = new JSONObject();
        json.put("page_index", pageNow);
        json.put("page_size", 50);
        json.put("modified_begin", modifiedBegin);
        json.put("modified_end", modifiedEnd);
//		json.put("i_ids", Arrays.asList("PMAA00-1R21JE-R001-2560"));
        params.put("biz", json.toJSONString());
        // 参数加密
        params.put("sign", refreshToken(params));

        // 接口调用
        ObjectMapper mapper = new ObjectMapper();
        String result = callApi(new URL(domainUrl + productQueryUrl), params);
        JstProductResDTO jstProductResDTO = mapper.readValue(result, JstProductResDTO.class);

        if(0 != jstProductResDTO.getCode()) {
            log.error("--------------- 聚水潭商品列表接口, 请求失败: {} ---------------", result);
            return null;
        }

        return jstProductResDTO;
    }

    /**
     * 库存查询
     * @author daigb
     * @date:  2022-03-01 15:00
     */
    public JstProductStockResDTO selectStock(String skuIds) throws MalformedURLException, IOException {
        if(StringUtil.isEmpty(skuIds)) {
            log.error("--------------- 聚水潭商品库存接口, 无skuId直接返回 ---------------");
            return null;
        }
        long second = Instant.now().getEpochSecond();
        Map<String, String> params = getParams(second);
        String accessToken = redisUtil.get(Constants.JST_ACCESS_TOKEN);

        // 获取签名
        params.put("access_token", accessToken);
        params.put("version", "2");
        // 业务参数
        JSONObject json = new JSONObject();
        json.put("page_index", 1);
        json.put("page_size", 50);
        json.put("sku_ids", skuIds);
        params.put("biz", json.toJSONString());
        // 参数加密
        params.put("sign", refreshToken(params));

        // 接口调用
        ObjectMapper mapper = new ObjectMapper();
        String result = callApi(new URL(domainUrl + inventoryQueryUrl), params);
        JstProductStockResDTO jstProductStockResDTO = mapper.readValue(result, JstProductStockResDTO.class);
        if(0 != jstProductStockResDTO.getCode()) {
            log.error("--------------- 聚水潭商品库存接口, 请求失败: {} ---------------", result);
            return null;
        }

        return jstProductStockResDTO;
    }

    /**
     * 接口调用
     * @author daigb
     * @date:  2022-02-23 16:07
     */
    private static String callApi(URL url, Map<String, String> params) throws IOException {
        String query = buildQuery(params, CHARSET_UTF8);
        byte[] content = {};
        if (query != null) {
            content = query.getBytes(CHARSET_UTF8);
        }

        HttpURLConnection conn = null;
        OutputStream out = null;
        String rsp = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Host", url.getHost());
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + CHARSET_UTF8);
            out = conn.getOutputStream();
            out.write(content);
            rsp = getResponseAsString(conn);
        } finally {
            if (out != null) {
                out.close();
            }
            if (conn != null) {
                conn.disconnect();
            }
        }

        return rsp;
    }

    private static String buildQuery(Map<String, String> params, String charset) throws IOException {
        if (params == null || params.isEmpty()) {
            return null;
        }

        StringBuilder query = new StringBuilder();
        Set<Map.Entry<String, String>> entries = params.entrySet();
        boolean hasParam = false;

        for (Map.Entry<String, String> entry : entries) {
            String name = entry.getKey();
            String value = entry.getValue();
            // 忽略参数名或参数值为空的参数
            if (isNotEmpty(name) && isNotEmpty(value)) {
                if (hasParam) {
                    query.append("&");
                } else {
                    hasParam = true;
                }

                query.append(name).append("=").append(URLEncoder.encode(value, charset));
            }
        }

        return query.toString();
    }

    private static String getResponseAsString(HttpURLConnection conn) throws IOException {
        String charset = getResponseCharset(conn.getContentType());
        if (conn.getResponseCode() < 400) {
            String contentEncoding = conn.getContentEncoding();
            if (CONTENT_ENCODING_GZIP.equalsIgnoreCase(contentEncoding)) {
                return getStreamAsString(new GZIPInputStream(conn.getInputStream()), charset);
            } else {
                return getStreamAsString(conn.getInputStream(), charset);
            }
        } else {// Client Error 4xx and Server Error 5xx
            throw new IOException(conn.getResponseCode() + " " + conn.getResponseMessage());
        }
    }

    private static String getStreamAsString(InputStream stream, String charset) throws IOException {
        try {
            Reader reader = new InputStreamReader(stream, charset);
            StringBuilder response = new StringBuilder();

            final char[] buff = new char[1024];
            int read = 0;
            while ((read = reader.read(buff)) > 0) {
                response.append(buff, 0, read);
            }

            return response.toString();
        } finally {
            if (stream != null) {
                stream.close();
            }
        }
    }


    private static String getResponseCharset(String ctype) {
        String charset = CHARSET_UTF8;

        if (isNotEmpty(ctype)) {
            String[] params = ctype.split(";");
            for (String param : params) {
                param = param.trim();
                if (param.startsWith("charset")) {
                    String[] pair = param.split("=", 2);
                    if (pair.length == 2) {
                        if (isNotEmpty(pair[1])) {
                            charset = pair[1].trim();
                        }
                    }
                    break;
                }
            }
        }

        return charset;
    }

    private static boolean isNotEmpty(String value) {
        int strLen;
        if (value == null || (strLen = value.length()) == 0) {
            return false;
        }
        for (int i = 0; i < strLen; i++) {
            if ((Character.isWhitespace(value.charAt(i)) == false)) {
                return true;
            }
        }
        return false;
    }

    private Map<String, String> getParams(long second) {
        Map<String, String> params = new HashMap<>();
        params.put("app_key", appKey);
        params.put("timestamp", second + "");
        params.put("charset", CHARSET_UTF8);

        return params;
    }

    /**
     * 刷新令牌
     * @author daigb
     * @date:  2022-02-23 14:42
     */
    private String refreshToken(Map<String, String> params) {
        return getSign(appSecret, params);
    }

    /**
     * 签名
     * @author daigb
     * @date:  2022-02-23 14:44
     */
    public static String getSign(String app_secret, Map<String, String> params) {
        try {
            String sortedStr = getSortedParamStr(params);
            String paraStr = app_secret + sortedStr;

            return createSign(paraStr);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return StringUtils.EMPTY;
    }

    /**
     * 构造自然排序请求参数
     *
     * @param params 请求
     * @return 字符串
     */
    private static String getSortedParamStr(Map<String, String> params) throws UnsupportedEncodingException {
        Set<String> sortedParams = new TreeSet<>(params.keySet());

        StringBuilder strB = new StringBuilder();
        // 排除sign和空值参数
        for (String key : sortedParams) {
            if ("sign".equalsIgnoreCase(key)) {
                continue;
            }

            String value = params.get(key);

            if (StringUtils.isNotEmpty(value)) {
                strB.append(key).append(value);
            }
        }
        return strB.toString();
    }

    /**
     * 生成新sign
     *
     * @param str 字符串
     * @return String
     */
    private static String createSign(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] buf = new char[j * 2];
            int k = 0;
            int i = 0;
            while (i < j) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
                i++;
            }
            return new String(buf);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
